﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web.UI.WebControls;
using System.Windows.Controls;
using System.Windows.Shapes;
using System.Xml;
using System.Xml.Linq;
using KqAutoDesign.Base;
using KqAutoDesign.Models;

namespace KqAutoDesign
{
    public class GlobalConfigXMLManager
    {
        static GlobalConfigXMLManager() 
        {
            if(string.IsNullOrEmpty(App.GlobalConfigs.ConfigFilePath))
            {
                App.GlobalConfigs.ConfigFilePath = Environment.CurrentDirectory + @"\Config.xml";
            }
            _xmlDoc = new XmlDocument();
            _xmlDoc.Load(App.GlobalConfigs.ConfigFilePath);
        }

        private static XmlDocument _xmlDoc;
        public void GetConfigApps()
        {
            if(_xmlDoc == null)
                return ;
            var registedAppsNode = _xmlDoc.SelectSingleNode("/App/RegistedApps");
            App.GlobalConfigs.Apps = new ObservableCollection<RegistedApp>();
            foreach (XmlNode node in registedAppsNode.ChildNodes)
            {
                if (node.Name == "RegistedApp")
                {
                    RegistedApp app = new RegistedApp();
                    app.Name = node.Attributes["name"]?.Value;
                    app.Description = node.Attributes["description"]?.Value;
                    app.Id = int.Parse(node.Attributes["id"]?.Value);
                    app.AppType = node.Attributes["type"]?.Value;
                    app.StartPath = node.Attributes["startPath"]?.Value;
                    app.Icon = node.Attributes["icon"]?.Value;
                    App.GlobalConfigs.Apps.Add(app);
                }
            }
        }

        public void GetConfigWorkflows()
        {
            if (_xmlDoc == null)
                return;
            var registedWorkflows = _xmlDoc.SelectSingleNode("/App/WorkFlows");
            App.GlobalConfigs.WorkFlowTemplates = new ObservableCollection<WorkFlowBase>();
            foreach (XmlNode node in registedWorkflows.ChildNodes)
            {
                if(node.Name == "WorkFlow")
                {
                    WorkFlowBase workFlowBase = new WorkFlowBase();
                    workFlowBase.WorkFlowState = WorkFlowStateEnum.NoPlan;
                    workFlowBase.Id = int.Parse(node.Attributes["id"]?.Value);
                    workFlowBase.Name = node.Attributes["name"]?.Value;
                    workFlowBase.Description = node.Attributes["description"]?.Value;
                    workFlowBase.Modules = new ObservableCollection<ModuleBase>();
                    foreach (XmlNode nd in node.ChildNodes)
                    {
                        if (nd.Name == "Module")
                        {
                            ModuleBase moduleBase = new ModuleBase();
                            moduleBase.FunctionScripts = new ObservableCollection<Script>();
                            moduleBase.Id = int.Parse(nd.Attributes["id"]?.Value);
                            moduleBase.Name = nd.Attributes["name"]?.Value;
                            moduleBase.Description = nd.Attributes["description"]?.Value;
                            string appName = nd.Attributes["registedApp"]?.Value;
                            moduleBase.App = App.GlobalConfigs.Apps.FirstOrDefault(x => x.Name == appName);
                            moduleBase.Icon= moduleBase.App?.Icon;
                            foreach (XmlNode snd in nd.ChildNodes)
                            {
                                if(snd.Name== "BatchScript")
                                {
                                    Script script = new Script();
                                    try
                                    {
                                        script.Id = int.Parse(snd.Attributes["id"]?.Value);
                                    }
                                    catch 
                                    {
                                        script.Id = null;
                                    }
                                    script.Sctp = ScriptTypeEnum.BatchScript;
                                    script.Name = snd.Attributes["name"]?.Value;
                                    script.Description = snd.Attributes["description"]?.Value;
                                    script.FileName = snd.Attributes["fileName"]?.Value;
                                    script.ScriptParams = GetParamsFromScript(snd);
                                    moduleBase.BatchScript = script;
                                }
                                else if(snd.Name== "FunctionScripts")
                                {
                                    foreach (XmlNode ssnd in snd.ChildNodes)
                                    {
                                        Script script = new Script();
                                        script.Sctp = ScriptTypeEnum.FunctionScript;
                                        try
                                        {
                                            script.Id = int.Parse(ssnd.Attributes["id"]?.Value);
                                        }
                                        catch
                                        {
                                            script.Id = null;
                                        }
                                        script.Name = ssnd.Attributes["name"]?.Value;
                                        script.Description = ssnd.Attributes["description"]?.Value;
                                        script.FileName = ssnd.Attributes["fileName"]?.Value;
                                        script.ScriptParams = GetParamsFromScript(ssnd);
                                        moduleBase.FunctionScripts.Add(script);
                                    }
                                }
                            }
                            workFlowBase.Modules.Add(moduleBase);
                        }
                    }
                    App.GlobalConfigs.WorkFlowTemplates.Add(workFlowBase);
                }
                App.GlobalConfigs.WorkFlowTemplates =new ObservableCollection<WorkFlowBase>(App.GlobalConfigs.WorkFlowTemplates.OrderBy(x=>x.Id));
            }
        }

        public ObservableCollection<ScriptParam> GetParamsFromScript(XmlNode scriptNode)
        {
            ObservableCollection <ScriptParam> results = new ObservableCollection<ScriptParam>();
            foreach (XmlNode pms in scriptNode.ChildNodes)
            {
                if(pms.Name == "Params")
                {
                    foreach (XmlNode pm in pms.ChildNodes)
                    {
                        ScriptParam param = new ScriptParam();
                        param.Id = int.Parse(pm.Attributes["id"]?.Value);
                        param.Name = pm.Attributes["name"]?.Value;
                        var typStr = pm.Attributes["type"]?.Value;
                        switch (typStr)
                        {
                            case "string":
                                param.ParamType = ParamTypeEnum.StringType;
                                break;
                            case "int":
                                param.ParamType = ParamTypeEnum.IntType;
                                break;
                            case "double":
                                param.ParamType = ParamTypeEnum.DoubleType;
                                break;
                            case "bool":
                                param.ParamType = ParamTypeEnum.BoolType;
                                break;
                        }
                        param.DefaultValue = pm.Attributes["default"]?.Value;
                        param.Description = pm.Attributes["description"]?.Value;
                        param.Unit = pm.Attributes["unit"]?.Value;
                        results.Add(param);
                    }
                }
            } 
            return results;
        }

        public void ModifyOneNodeAttributes(string ndPath, Dictionary<string,string> kvps)
        {
            if (_xmlDoc == null) return ;
            XmlNode node = _xmlDoc.SelectSingleNode(ndPath);
            if (node == null) return ;
            foreach (KeyValuePair<string,string> kvp in kvps)
            {
                XmlAttribute attr = node.Attributes[kvp.Key];
                if (attr == null)
                {
                    attr = _xmlDoc.CreateAttribute(kvp.Key);
                    attr.Value = kvp.Value;
                    node.Attributes.Append(attr);
                }
                else
                {
                    attr.Value = kvp.Value;
                }
            }
            _xmlDoc.Save(App.GlobalConfigs.ConfigFilePath);
        }

        public void AddRegistedApp(RegistedApp app)
        {
            if (_xmlDoc == null) return;
            XmlNode pNode = _xmlDoc.SelectSingleNode("/App/RegistedApps");
            if (pNode == null) return ;
            XmlElement newElement = _xmlDoc.CreateElement("RegistedApp");
            newElement.SetAttribute("id", app.Id.ToString());
            newElement.SetAttribute("name", app.Name);
            newElement.SetAttribute("startPath", app.StartPath);
            newElement.SetAttribute("icon", app.Icon);
            newElement.SetAttribute("description", app.Description);
            pNode.AppendChild(newElement);
            _xmlDoc.Save(App.GlobalConfigs.ConfigFilePath);
        }

        internal void WriteBatchScriptParams(int wfid, int mdid, int? scriptid, ObservableCollection<ScriptParam> pms)
        {
            if (_xmlDoc == null) return;
            XmlNode pmsNd = _xmlDoc.SelectSingleNode(string.Format("/App/WorkFlows/WorkFlow[@id='{0}']/Module[@id='{1}']/BatchScript[@id='{2}']", wfid,mdid,scriptid));
            if (pmsNd == null) return;
            for (int i = pmsNd.ChildNodes.Count - 1; i >= 0; i--)
            {
                pmsNd.RemoveChild(pmsNd.ChildNodes[i]);
            }
            XmlElement paramsNode = CreateScriptParamsElement(_xmlDoc, pms);
            pmsNd.AppendChild(paramsNode);
            _xmlDoc.Save(App.GlobalConfigs.ConfigFilePath);
        }

        internal void WriteFunctionScriptParams(int wfid, int mdid, int? scriptid, ObservableCollection<ScriptParam> pms)
        {
            if (_xmlDoc == null) return;
            XmlNode pmsNd = _xmlDoc.SelectSingleNode(string.Format("/App/WorkFlows/WorkFlow[@id='{0}']/Module[@id='{1}']/FunctionScripts/FunctionScript[@id='{2}']", wfid, mdid, scriptid));
            if (pmsNd == null) return;
            for (int i = pmsNd.ChildNodes.Count - 1; i >= 0; i--)
            {
                pmsNd.RemoveChild(pmsNd.ChildNodes[i]);
            }
            XmlElement paramsNode = CreateScriptParamsElement(_xmlDoc, pms);
            pmsNd.AppendChild(paramsNode);
            _xmlDoc.Save(App.GlobalConfigs.ConfigFilePath);
        }

        public void AddWorkflow(WorkFlowBase wf)
        {
            if (_xmlDoc == null) return;
                XmlNode pNode = _xmlDoc.SelectSingleNode("/App/WorkFlows");
            if (pNode == null) return;
            foreach (XmlNode node in pNode.ChildNodes)
            {
                if (node.Attributes["id"].Value==wf.Id.ToString())
                {
                    pNode.RemoveChild(node);
                    break;
                }
            }
            XmlElement workflow = _xmlDoc.CreateElement("WorkFlow");
            workflow.SetAttribute("id", wf.Id.ToString());
            workflow.SetAttribute("name", wf.Name);
            workflow.SetAttribute("description", wf.Description);
            foreach (var md in wf.Modules)
            {
                XmlElement module = _xmlDoc.CreateElement("Module");
                module.SetAttribute("id", md.Id.ToString());
                module.SetAttribute("name", md.Name);
                module.SetAttribute("description", md.Description);
                module.SetAttribute("registedApp", md.App.Name);
                XmlElement bcElement = CreateScriptElement(_xmlDoc,md.BatchScript, "BatchScript");
                XmlElement fcElement = CreateFunctionScriptsElement(_xmlDoc, md.FunctionScripts);
                module.AppendChild(bcElement);
                module.AppendChild(fcElement);
                workflow.AppendChild(module);
            }
            pNode.AppendChild(workflow);
            _xmlDoc.Save(App.GlobalConfigs.ConfigFilePath);
        }

        private XmlElement CreateScriptElement(XmlDocument xmd,Script script,string scriptType)
        {
            XmlElement batchScript = _xmlDoc.CreateElement(scriptType);
            batchScript.SetAttribute("id", script.Id.ToString());
            batchScript.SetAttribute("name", script.Name);
            batchScript.SetAttribute("description", script.Description);
            batchScript.SetAttribute("fileName", script.FileName);

            XmlElement paramsNode = CreateScriptParamsElement(xmd, script.ScriptParams);
            batchScript.AppendChild(paramsNode);
            return batchScript;
        }

        private XmlElement CreateFunctionScriptsElement(XmlDocument xmd, IList<Script> scripts)
        {
            XmlElement fucScripts = _xmlDoc.CreateElement("FunctionScripts");
            if(scripts == null) return fucScripts; 
            foreach (var script in scripts)
            {
                XmlElement bcElement = CreateScriptElement(_xmlDoc, script, "FunctionScript");
                fucScripts.AppendChild(bcElement);
            }
            return fucScripts;
        }

        private XmlElement CreateScriptParamsElement(XmlDocument xmd, IList<ScriptParam> scriptParams)
        {
            XmlElement paramsNode = xmd.CreateElement("Params");
            if(scriptParams==null) 
                return paramsNode; 
            foreach (ScriptParam pm in scriptParams)
            {
                XmlElement newElement = _xmlDoc.CreateElement("param");
                newElement.SetAttribute("id", pm.Id.ToString());
                newElement.SetAttribute("name", pm.Name);
                newElement.SetAttribute("type", pm.ParamType.GetDescription());
                newElement.SetAttribute("description", pm.Description);
                newElement.SetAttribute("default", pm.DefaultValue);
                paramsNode.AppendChild(newElement);
            }
            return paramsNode;
        }

        public bool DeleteWorkflow(string wfId, string wfName)
        {
            if (_xmlDoc == null) return false;
            XmlNode wfsNd = _xmlDoc.SelectSingleNode("/App/WorkFlows");
            if(wfsNd == null) return false;
            XmlNode pmsNd = _xmlDoc.SelectSingleNode(string.Format("/App/WorkFlows/WorkFlow[@id='{0}' and @name='{1}']", wfId, wfName));
            if (pmsNd == null) return false;
            wfsNd.RemoveChild(pmsNd);
            _xmlDoc.Save(App.GlobalConfigs.ConfigFilePath);
            return true;
        }
    } 

    public class ResourcesLoader
    {
        public ResourcesLoader() { }
        public static void Load()
        {
            App.GlobalConfigs.Resources = new Resources();
            var icons = Directory.GetFiles(Environment.CurrentDirectory + @"\Resources\Icons");
            foreach (var icon in icons)
            {
                IconInfo info = new IconInfo(icon);
                App.GlobalConfigs.Resources.Icons.Add(info);
            }
        }
    }

}
